﻿namespace PageTypeBuilderUI.CustomProperties
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using EPiServer.Core;
    using EPiServer.DataAbstraction;
    using EPiServer.UI.WebControls;
    using EPiServer.Web.PageExtensions;
    using EPiServer.Web.PropertyControls;
    using PageTypeBuilder;
    using PageTypeBuilder.Abstractions;
    using PageTypeBuilder.Discovery;
    using Reflection;
    using Tab = PageTypeBuilder.Tab;

    public class PropertyGroupPropertyControl : PropertyDataControl
    {

        private List<IPropertyControl> _propertyControls;
        private readonly List<Control> _tabPanels = new List<Control>();
        private Panel _container;
        private PlaceHolder _formPlaceHolder;

        public override void CreateEditControls()
        {
            Type pageTypeType = PageTypeResolver.Instance.GetPageTypeType(CurrentPage.PageTypeID);

            if (pageTypeType == null)
                return;

            if (!Name.EndsWith("_Container"))
                return;

            string propertyName = Name.Substring(0, Name.Length - "_Container".Length);

            PropertyInfo propertyGroup = pageTypeType.GetPageTypePropertyGroupProperties()
                .Where(current => string.Equals(current.Name, propertyName)).FirstOrDefault();

            if (propertyGroup == null)
                return;

            _propertyControls = new List<IPropertyControl>();
            IPageType pageType = new WrappedPageType(PageType.Load(CurrentPage.PageTypeID));
            List<PageTypePropertyDefinition> pageTypePropertyDefinitions = pageType.GetPropertyGroupPropertyDefinitions(propertyGroup)
                .OrderBy(current => current.PageTypePropertyAttribute.SortOrder)
                .ToList();

            CreatePlaceHolderControls();

            Table table = new Table { CellSpacing = 0, CssClass = "epi-formLayout" };
            table.Style["display"] = "block";
            _formPlaceHolder.Controls.Add(table);

            List<Tab> tabs = pageTypePropertyDefinitions
                    .Select(current => GetTab (propertyGroup.PropertyType.GetProperty(current.Name.Substring(current.Name.IndexOf("-") + 1)).GetPropertyAttribute<PageTypePropertyAttribute>()))
                    .Where(current => current != null)
                    .OrderBy(current => current.SortIndex)
                    .Distinct(new TabComparer())
                    .ToList();

            TabStrip tabStrip;
            Panel tabsViewPanel;
            CreateTabs(propertyGroup, out tabStrip, out tabsViewPanel, tabs);

            if (tabs.Count == 0)
            {
                foreach (PageTypePropertyDefinition pageTypePropertyDefinition in pageTypePropertyDefinitions)
                    AddEditingRow(pageType, table, pageTypePropertyDefinition);
            }
            else
            {
                IEnumerable<string> nonTabbedPropertyKeys = pageTypePropertyDefinitions
                        .Where(current => string.IsNullOrEmpty(GetTabName(propertyGroup.PropertyType.GetProperty(current.Name.Substring(current.Name.IndexOf("-") + 1)).GetPropertyAttribute<PageTypePropertyAttribute>())))
                        .Select(current => current.Name)
                        .ToList();

                foreach (string key in nonTabbedPropertyKeys)
                    AddEditingRow(pageType, table, pageTypePropertyDefinitions.Where(current => current.Name.Equals(key)).First());

                _formPlaceHolder.Controls.Add(tabsViewPanel);
                _formPlaceHolder.Controls.AddAt(_formPlaceHolder.Controls.Count - 1, tabStrip);

                int index = 1;

                foreach (Tab tab in tabs)
                {
                    string tabName = tab.Name;
                    Panel tabPanel = new Panel { ID = propertyGroup.Name.Replace("-", "_") + "_TabViewPanel_" + index };
                    tabsViewPanel.Controls.Add(tabPanel);
                    _tabPanels.Add(tabPanel);

                    Table tabTable = new Table { CellSpacing = 0 };
                    table.Style["display"] = "block";
                    tabPanel.Controls.Add(tabTable);

                    // Need to add properties that match the tab?
                    IEnumerable<string> tabbedPropertyKeys = pageTypePropertyDefinitions
                        .Where(current => string.Equals(GetTabName(propertyGroup.PropertyType.GetProperty(current.Name.Substring(current.Name.IndexOf("-") + 1)).GetPropertyAttribute<PageTypePropertyAttribute>()), tabName))
                        .Select(current => current.Name)
                        .ToList();

                    foreach (string key in tabbedPropertyKeys)
                        AddEditingRow(pageType, tabTable, pageTypePropertyDefinitions.Where(current => current.Name.Equals(key)).First());

                    index++;
                }

                tabStrip.SetSelectedTab(0);
            }
        }

        private void CreatePlaceHolderControls()
        {
            _container = new Panel { ID = PropertyData.Name + "_Container" };
            Controls.Add(_container);

            _formPlaceHolder = new PlaceHolder();
            _container.Controls.Add(_formPlaceHolder);
        }

        private void AddEditingRow(IPageType pageType, Table container, PageTypePropertyDefinition pageTypePropertyDefinition)
        {
            // add row panel
            TableRow tableRow = new TableRow();
            container.Rows.Add(tableRow);

            // add label text
            TableHeaderCell headerCell = new TableHeaderCell { CssClass = "EP-tableCaptionCell" };

            PageDefinition pageDefinition = pageType.Definitions
                .Where(current => string.Equals(current.Name, pageTypePropertyDefinition.Name))
                .First();

            string propertyName = pageTypePropertyDefinition.Name;

            HtmlGenericControl label = new HtmlGenericControl("span") { InnerHtml = GetLabelText(CurrentPage.Property[propertyName].TranslateDisplayName()) };

            if (pageDefinition.Required)
                label.InnerHtml += "<span style=\"margin-left:3px\">*</span>";

            headerCell.Controls.Add(label);

            if (!pageDefinition.LanguageSpecific)
                headerCell.Controls.Add(GenerateCommonImage());
            
            tableRow.Cells.Add(headerCell);

            IPropertyControl propertyControl = PropertyControlClassFactory.Instance.CreatePropertyControl(CurrentPage.Property[propertyName]);
            TableCell cell = new TableCell();
            cell.Style["padding-bottom"] = "0.3em";

            if (propertyControl.RowLayout == TableRowLayout.Wide)
            {
                tableRow = new TableRow();
                container.Rows.Add(tableRow);
                cell.ColumnSpan = 2;
            }

            tableRow.Cells.Add(cell);
            cell.ID = propertyName + "_ContainerCell";

            Control control = propertyControl as Control;
            control.ID = propertyName.Replace("-", "_");
            propertyControl.PropertyData = CurrentPage.Property[propertyName];
            propertyControl.Properties = CurrentPage.Property;
            cell.Controls.Add(control);
            propertyControl.RenderType = RenderType.Edit;
            propertyControl.SetupControl();

            _propertyControls.Add(propertyControl);
        }

        public override void ApplyEditChanges()
        {
            if (_propertyControls == null)
                return;

            foreach (IPropertyControl propertyControl in _propertyControls)
            {
                bool propertyStringControl = false;

                try
                {
                    if (propertyControl is PropertyStringControl)
                    {
                        propertyStringControl = true;
                        (propertyControl as PropertyStringControl).ApplyEditChanges();
                    }
                    else
                        propertyControl.ApplyChanges();
                }
                catch (Exception)
                {
                    if (propertyStringControl)
                        propertyControl.ApplyChanges();
                    else
                        throw;
                }
            }
        }

        private string GetLabelText(string editCaption)
        {
            if (editCaption.Contains("-"))
                editCaption = editCaption.Substring(editCaption.IndexOf("-") + 1).Trim();

            if (editCaption.Contains(":"))
                editCaption = editCaption.Substring(editCaption.IndexOf(":") + 1).Trim();

            return editCaption;
        }

        private void CreateTabs(PropertyInfo propertyGroup, out TabStrip tabStrip, out Panel tabsViewPanel, ICollection<Tab> tabs)
        {
            tabStrip = null;
            tabsViewPanel = null;

            if (tabs.Count <= 0)
                return;

            string propertyGroupName = propertyGroup.Name.Replace("-", "_");

            tabStrip = new TabStrip
            {
                ID = propertyGroupName + "_Tabs",
                GeneratesPostBack = false,
                TargetID = propertyGroupName + "_Tab_Panel"
            };

            tabsViewPanel = new Panel { CssClass = "epi-padding-small" };
            tabsViewPanel.Style["border-left"] = "1px solid #4D4D4D";
            tabsViewPanel.Style["border-right"] = "1px solid #4D4D4D";
            tabsViewPanel.Style["border-bottom"] = "1px solid #4D4D4D";
            tabsViewPanel.ID = propertyGroupName + "_Tab_Panel";

            int index = 1;

            foreach (Tab currentTab in tabs)
            {
                EPiServer.UI.WebControls.Tab tab = new EPiServer.UI.WebControls.Tab
                {
                    Sticky = true,
                    ID = propertyGroupName + "_Tab_" + index,
                    Text = currentTab.Name,
                    Visible = CurrentPage.QueryDistinctAccess(currentTab.RequiredAccess)
                };

                tabStrip.Controls.Add(tab);
                index++;
            }
        }

        private string GetTabName(PageTypePropertyAttribute attribute)
        {
            Tab tab = GetTab(attribute);
            return tab == null ? string.Empty : tab.Name;
        }

        private Tab GetTab(PageTypePropertyAttribute attribute)
        {
            if (attribute.Tab == null)
                return null;

            if (!attribute.Tab.IsSubclassOf(typeof(Tab)))
                throw new Exception("The Tab PageTypePropertyAttribute property must be set to a class that inherits PageTypeBuilder.Tab");

            return Activator.CreateInstance(attribute.Tab) as Tab;
        }

        private HtmlImage GenerateCommonImage()
        {
            HtmlImage htmlImage = new HtmlImage();
            htmlImage.Src = ThemeUtility.GetImageThemeUrl(this.Page, "Tools/LanguageProperty.gif");
            htmlImage.Alt = LanguageManager.Instance.Translate("/system/editutil/languagecommonpropertyicontooltiporiginal");
            return htmlImage;
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (RenderType == RenderType.Default || _tabPanels == null)
                return;

            foreach (Panel panel in _tabPanels.Cast<Panel>().Where(panel => panel.Attributes["onclick"] != null))
                panel.Attributes.Remove("onclick");
        }
    }

    public class TabComparer : IEqualityComparer<Tab>
    {
        public bool Equals(Tab x, Tab y)
        {
            if (ReferenceEquals(x, y)) return true;

            if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
                return false;

            return x.GetType().Equals(y.GetType());
        }

        public int GetHashCode(Tab x)
        {
            return ReferenceEquals(x, null) ? 0 : (x.Name ?? string.Empty).GetHashCode();
        }
    }

}
